package de.koller.worlders.engine.entity.mo.phsics;

import de.koller.oceanFramework.math.Vec;
import de.koller.oceanFramework.util.OceanSet;
import de.koller.worlders.WSea;
import de.koller.worlders.engine.entity.IAABB;
import de.koller.worlders.engine.entity.plattform.Plattform;
import de.koller.worlders.engine.tiles.Tile;
import java.util.*;

public class Physics extends WSea {

	private final OceanSet<PhysicsMO> pmos = new OceanSet<>();
	
	@Override
	protected void init() {
		super.init();
		
	}

	@Override
	protected void update( int elapsedTime ) {
		super.update( elapsedTime );
		
		if( wroot.getDebug().isActive() )
			return;
		
		for( PhysicsMO mo : pmos )
			setCalcDistance( mo );
		for( PhysicsMO mo : pmos )
			if( mo.isCalcDistance() ) mo.getMomov().velocityStep( elapsedTime );
		
		for( PhysicsMO mo : pmos )
			if( mo.isCalcDistance() ) mo.getMomov().collisionStepX();
		for( PhysicsMO mo : pmos )
			if( mo.isCalcDistance() ) mo.getMomov().handleContacts();
		for( PhysicsMO mo : pmos )
			if( mo.isCalcDistance() ) mo.getMomov().positionStepX();
		
		for( PhysicsMO mo : pmos )
			if( mo.isCalcDistance() ) mo.getMomov().collisionStepY();
		for( PhysicsMO mo : pmos )
			if( mo.isCalcDistance() ) mo.getMomov().handleContacts();
		for( PhysicsMO mo : pmos )
			if( mo.isCalcDistance() ) mo.getMomov().positionStepY();
		
		for( PhysicsMO mo : pmos )
			if( mo.isCalcDistance() ) this.checkOverlap( mo );
		
		pmos.update();
	}

	public OceanSet<PhysicsMO> getPmos() {
		return pmos;
	}

	private void checkOverlap( PhysicsMO mo ) {
		if( !mo.isOverlapResolver() )
			return;
		
		float[] rx = isOverlap( mo, true );
		float[] ry = isOverlap( mo, false );
		if( rx == null && ry == null )
			return;
		
		float disXL = Float.MAX_VALUE; //0
		float disXR = Float.MAX_VALUE; //1
		float disYU = Float.MAX_VALUE; //2
		float disYD = Float.MAX_VALUE; //3
		
		if( rx != null ) {
			disXL = mo.getPosition().getXf() - (rx[0] - mo.getSize().getXf());
			disXR = rx[1] - mo.getPosition().getXf();
		}
		if( ry != null ) {
			disYU = mo.getPosition().getYf() - (ry[0] - mo.getSize().getYf());
			disYD = ry[1] - mo.getPosition().getYf();
		}
		
		Vec savedPos = mo.getPosition().newInstance();
		List<OverlapDis> odis = new ArrayList<>();
		if( disXL != Float.MAX_VALUE ) odis.add( new OverlapDis( disXL, 0 ) );
		if( disXR != Float.MAX_VALUE ) odis.add( new OverlapDis( disXR, 1 ) );
		if( disYU != Float.MAX_VALUE ) odis.add( new OverlapDis( disYU, 2 ) );
		if( disYD != Float.MAX_VALUE ) odis.add( new OverlapDis( disYD, 3 ) );
		Collections.sort( odis );
				
		for( OverlapDis o : odis ) {
			if( o.dir == 0 ) moveTo( rx, mo, true, true );
			else if( o.dir == 1 ) moveTo( rx, mo, true, false );
			else if( o.dir == 2 ) moveTo( ry, mo, false, true );
			else if( o.dir == 3 ) moveTo( ry, mo, false, false );
			
			boolean od = o.dir == 0 || o.dir == 1;
			if( isOverlap( mo, od ) != null )
				mo.getPosition().set( savedPos );
			else
				break;
		}
	
	}
	
	private void moveTo( float[] r, PhysicsMO mo, boolean x, boolean left ) {
		if( x )
			if( left )
				mo.getPosition().setX( r[0] - mo.getSize().getX() - 0.001 );
			else
				mo.getPosition().setX( r[1] + 0.001 );
		else
			if( left )
				mo.getPosition().setY( r[0] - mo.getSize().getY() - 0.001 );
			else
				mo.getPosition().setY( r[1] + 0.001 );
	}
	
	public float[] isOverlap( PhysicsMO mo, boolean x ) {
		Set<IAABB> aabb = new HashSet<>();
		CollisionIterator.updateAABBs( aabb, mo );
		
		boolean iso = false;
		float[] r = new float[] { Float.MAX_VALUE, Float.MIN_VALUE };
		
		for( IAABB a : aabb ) {
			if( a instanceof Tile == false )
				continue;
			Tile t = (Tile)a;
			if( t.getTileEntity() instanceof Plattform )
				if( CollisionIterator.isAABBIntersection( mo, a ) ) {
					iso = true;
					if( x ) {
						r[0] = Math.min( a.getPosition().getXf(), r[0] );
						r[1] = Math.max( a.getPosition().getXf() + a.getSize().getXf(), r[1] );
					} else {
						r[0] = Math.min( a.getPosition().getYf(), r[0] );
						r[1] = Math.max( a.getPosition().getYf() + a.getSize().getYf(), r[1] );
					}
					continue;
				}
		}
		
		if( !iso )
			return null;
		else
			return r;
	}

	private void setCalcDistance( PhysicsMO mo ) {
		float dx = Math.abs( wroot.gamePlayer.getGPO().getAABB().getPosition().getXf() - mo.getPosition().getXf() );
		float dy = Math.abs( wroot.gamePlayer.getGPO().getAABB().getPosition().getYf() - mo.getPosition().getYf() );
		mo.setCurrentCalcDistance( Math.max( dx, dy ) );
	}
	
	private static class OverlapDis implements Comparable<OverlapDis> {
		private final float distance;
		private final int dir;

		public OverlapDis( float distance, int dir ) {
			this.distance = Math.abs( distance );
			this.dir = dir;
		}

		@Override
		public int compareTo( OverlapDis o ) {
			return Float.compare( distance, o.distance );
		}

		@Override
		public String toString() {
			return "OverlapDis{" + "distance=" + distance + ", dir=" + dir + '}';
		}
	}
	
}
